
#include <QColor>
#include <QGLWidget>
#include <QtOpenGL>

#include "object/material.h"

Material::Material(const QString& rkName, const QString& rkTextureFileName, float fShininess,
                   float fAmbientRed, float fAmbientGreen, float fAmbientBlue,
                   float fDiffuseRed, float fDiffuseGreen, float fDiffuseBlue,
                   float fSpecularRed, float fSpecularGreen, float fSpecularBlue)
    : m_bIsValid(false), m_kName(rkName), m_fShininess(fShininess),
      m_fAmbientRed(fAmbientRed), m_fAmbientGreen(fAmbientGreen), m_fAmbientBlue(fAmbientBlue),
      m_fDiffuseRed(fDiffuseRed), m_fDiffuseGreen(fDiffuseGreen), m_fDiffuseBlue(fDiffuseBlue),
      m_fSpecularRed(fSpecularRed), m_fSpecularGreen(fSpecularGreen), m_fSpecularBlue(fSpecularBlue)
{
    if(rkTextureFileName.isEmpty() || !m_kImage.load(rkTextureFileName))
    {
        // If image loading fail, the texture will be replaced with diffuse color.
        // Remove or improve as soon as textures are availables for map.
        new (&m_kImage) QImage(1, 1, QImage::Format_RGB32);
        m_kImage.fill(QColor(diffuseRed() * 255, diffuseGreen() * 255, diffuseBlue() * 255).rgb());
    }

    m_kImage = QGLWidget::convertToGLFormat(m_kImage);
    m_bIsValid = true;
}

Material::~Material()
{
}

bool Material::isValid() const
{
    return m_bIsValid;
}

QString Material::name() const
{
    return m_kName;
}

float Material::shininess() const
{
    return m_fShininess;
}

float Material::ambientRed() const
{
    return m_fAmbientRed;
}

float Material::ambientGreen() const
{
    return m_fAmbientGreen;
}

float Material::ambientBlue() const
{
    return m_fAmbientBlue;
}

float Material::diffuseRed() const
{
    return m_fDiffuseRed;
}

float Material::diffuseGreen() const
{
    return m_fDiffuseGreen;
}

float Material::diffuseBlue() const
{
    return m_fDiffuseBlue;
}

float Material::specularRed() const
{
    return m_fSpecularRed;
}

float Material::specularGreen() const
{
    return m_fSpecularGreen;
}

float Material::specularBlue() const
{
    return m_fSpecularBlue;
}

void Material::bind() const
{
    // Bind null material if invalid.
    if(!isValid())
    {
        glBindTexture(GL_TEXTURE_2D, 0);
        return;
    }

    glBindTexture(GL_TEXTURE_2D, m_uiId);

    // Ambient.
    float afAmbient[4];
    afAmbient[0] = ambientRed();
    afAmbient[1] = ambientGreen();
    afAmbient[2] = ambientBlue();
    afAmbient[3] = 1.f;

    glMaterialfv(GL_FRONT, GL_AMBIENT, afAmbient);

    // Diffuse.
    float afDiffuse[4];
    afDiffuse[0] = diffuseRed();
    afDiffuse[1] = diffuseGreen();
    afDiffuse[2] = diffuseBlue();
    afDiffuse[3] = 1.f;

    glMaterialfv(GL_FRONT, GL_DIFFUSE, afDiffuse);

    // Specular.
    float afSpecular[4];
    afSpecular[0] = specularRed();
    afSpecular[1] = specularGreen();
    afSpecular[2] = specularBlue();
    afSpecular[3] = 1.f;

    glMaterialfv(GL_FRONT, GL_SPECULAR, afSpecular);

    // Shininess.
    glMaterialf(GL_FRONT, GL_SHININESS, shininess());
}

void Material::unbind() const
{
    glBindTexture(GL_TEXTURE_2D, 0);

    // Ambient.
    float fAmbient[4];
    fAmbient[0] = 1.f;
    fAmbient[1] = 1.f;
    fAmbient[2] = 1.f;
    fAmbient[3] = 1.f;

    glMaterialfv(GL_FRONT, GL_AMBIENT, fAmbient);

    // Diffuse.
    float fDiffuse[4];
    fDiffuse[0] = 1.f;
    fDiffuse[1] = 1.f;
    fDiffuse[2] = 1.f;
    fDiffuse[3] = 1.0;

    glMaterialfv(GL_FRONT, GL_DIFFUSE, fDiffuse);

    // Specular.
    float fSpecular[4];
    fSpecular[0] = 1.f;
    fSpecular[1] = 1.f;
    fSpecular[2] = 1.f;
    fSpecular[3] = 1.f;

    glMaterialfv(GL_FRONT, GL_SPECULAR, fSpecular);

    // Shininess.
    glMaterialf(GL_FRONT, GL_SHININESS, 1.f);
}

void Material::loadGPU()
{
    glGenTextures(1, &m_uiId);
    glBindTexture(GL_TEXTURE_2D, m_uiId);

    glTexImage2D(GL_TEXTURE_2D, 0, 4,
                 m_kImage.width(),
                 m_kImage.height(),
                 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 m_kImage.bits());

    glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    m_bLoaded = true;
}

void Material::unloadGPU()
{
    glDeleteTextures(1, &m_uiId);
    m_bLoaded = false;
}

